home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / overview / ooptesample / utedocument.inc1.p < prev    next >
Encoding:
Text File  |  2000-06-23  |  25.0 KB  |  840 lines

  1. {---------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple TextEdit Sample Application
  6. #
  7. #    OOPTESample
  8. #
  9. #    UTEDocument.inc1.p        -    Pascal Source
  10. #
  11. #    Copyright © 1988, 1989 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:        
  15. #                    1.00                    04/89
  16. #                    1.10                    02/90
  17. #                    1.11                    10/92
  18. #
  19. #    Components:     
  20. #                    BuildOOPTESample            February 1, 1990
  21. #                    MTESample.p                    February 1, 1990
  22. #                    OOPTESample.make            February 1, 1990
  23. #                    TECommon.h                    February 1, 1990
  24. #                    TESampleGlue.a                February 1, 1990
  25. #                    TESample.r                    February 1, 1990
  26. #                    TMLRules.make                February 1, 1990
  27. #                    UApplication.p                February 1, 1990
  28. #                    UApplication.inc1.p            February 1, 1990
  29. #                    UDocument.p                    February 1, 1990
  30. #                    UDocument.inc1.p            February 1, 1990
  31. #                    UTEDocument.p                February 1, 1990
  32. #                    UTEDocument.inc1.p            February 1, 1990
  33. #                    UTESample.p                    February 1, 1990
  34. #                    UTESample.inc1.p            February 1, 1990
  35. #
  36. ---------------------------------------------------------------------}
  37.  
  38. CONST
  39.     kTextMargin                = 2;    {kTextMargin is the number of pixels we leave
  40.                                     blank at the edge of the window.}
  41.  
  42.     kMaxDocWidth            = 576;    {kMaxDocWidth is an arbitrary number used to specify
  43.                                     the width of the TERec's destination rectangle so that
  44.                                     word wrap and horizontal scrolling can be demonstrated.}
  45.  
  46.  
  47.     kMinDocDim                = 64;    {kMinDocDim is used to limit the minimum dimension
  48.                                     of a window when GrowWindow is called.}
  49.  
  50.  
  51.     kControlInvisible        = 0;
  52.     kControlVisible            = $FF;    {kControlInvisible is used to 'turn off' controls
  53.                                     (i.e., cause the control not to be redrawn as a result
  54.                                     of some Control Manager call such as SetControlValue)
  55.                                     by being put into the contrlVis field of the record.
  56.                                     kControlVisible is used the same way to 'turn on'
  57.                                     the control.}
  58.  
  59.     kScrollbarWidth            = 16;    {kScrollBarAdjust and kScrollBarWidth are used in
  60.                                     calculating values for control positioning and sizing.}
  61.     kScrollbarAdjust        = kScrollbarWidth - 1;
  62.     kGrowboxAdjust             = 15;
  63.  
  64.     kScrollTweek            = 2;    {kScrollTweek compensates for off-by-one requirements
  65.                                     of the scrollbars to have borders coincide with the
  66.                                     growbox.}
  67.  
  68.     kCRChar                    = chr(13);
  69.     kDelChar                = chr(8);    {kCrChar is used to match with a carriage return
  70.                                     when calculating the number of lines in the TextEdit
  71.                                     record. kDelChar is used to check for delete in
  72.                                     keyDowns.}
  73.  
  74.     kButtonScroll            = 4;    {kButtonScroll is how many pixels to scroll horizontally
  75.                                     when the button part of the horizontal scrollbar is
  76.                                     pressed.}
  77.  
  78.  
  79.     kMaxTELength            = 32000;    {kMaxTELength is an arbitrary number used to limit
  80.                                     the length of text in the TERec so that various errors
  81.                                     won't occur from too many characters in the text.}
  82.  
  83.     kTESlop                    = 1024;    { provides some extra security when pre-flighting
  84.                                     edit commands. }
  85.  
  86.     rVScroll                = 128;    { vertical scrollbar control }
  87.     rHScroll                = 129;    { horizontal scrollbar control }
  88.  
  89.     kTEDocErrStrings         = 129;    { id of our STR# for error strings }
  90.  
  91.     { The following are indicies into STR# resources. }
  92.     eNoMemory                = 1;
  93.     eNoSpaceCut                = 2;
  94.     eNoCut                    = 3;
  95.     eNoCopy                    = 4;
  96.     eExceedPaste            = 5;
  97.     eNoSpacePaste            = 6;
  98.     eNoWindow                = 7;
  99.     eExceedChar                = 8;
  100.     eNoPaste                = 9;
  101.  
  102. (********************************************************************************************)
  103. (*        G l o b a l   R o u t i n e s                                                        *)
  104. (********************************************************************************************)
  105. (*
  106.     Routines used by this class, which don't belong to the class since we use
  107.     them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
  108. *)
  109.  
  110.  
  111. PROCEDURE AsmClickLoop; EXTERNAL;
  112.  
  113. { Common algorithm for pinning the value of a control. It returns the actual amount }
  114. { the value of the control changed. }
  115.  
  116. {$S Main}
  117. PROCEDURE CommonAction(control:ControlHandle; VAR amount:integer);
  118. VAR
  119.     value, max: integer;
  120. BEGIN
  121.     value := GetControlValue(control);
  122.     max := GetControlMaximum(control);
  123.     amount := value - amount;
  124.     IF (amount <= 0) THEN
  125.         amount := 0
  126.     ELSE IF (amount >= max) THEN
  127.         amount := max;
  128.     SetControlValue(control, amount);
  129.     amount := value - amount;   { calculate true change }
  130. END; { CommonAction  }
  131.  
  132.  
  133. { Determines how much to change the value of the vertical scrollbar by and how }
  134. { much to scroll the TE record.}
  135.  
  136. {$S Main}
  137. PROCEDURE VActionProc(control:ControlHandle;part:integer);
  138. VAR
  139.     amount:integer;
  140.     window:WindowPtr;
  141.     hTE:TEHandle;
  142.     doc:TTEDocument;
  143. BEGIN
  144.     IF (part <> 0) THEN BEGIN
  145.         window := control^^.contrlOwner;
  146.         doc := TTEDocument(gApplication.DocList.FindDoc(window));
  147.         hTE := doc.GetTEHandle;
  148.         CASE part OF
  149.             inUpButton, inDownButton:        { one line  }
  150.                 amount := 1;
  151.             inPageUp, inPageDown:            { one page  }
  152.                 WITH hTE^^,viewRect DO
  153.                     amount := (bottom - top) DIV lineHeight;
  154.         END;
  155.         IF ((part = inDownButton) OR (part = inPageDown)) THEN
  156.             amount := -amount;        { reverse direction for a downer  }
  157.         CommonAction(control, amount);
  158.         IF (amount <> 0) THEN
  159.             TEScroll(0, amount*hTE^^.lineHeight, hTE);
  160.     END;
  161. END; { VActionProc }
  162.  
  163. { Determines how much to change the value of the horizontal scrollbar by and how }
  164. { much to scroll the TE record. }
  165.  
  166. {$S Main}
  167. PROCEDURE HActionProc(control:ControlHandle;part:integer);
  168. VAR
  169.     amount:integer;
  170.     window:WindowPtr;
  171.     hTE:TEHandle;
  172.     doc:TTEDocument;
  173. BEGIN
  174.     IF (part <> 0) THEN BEGIN
  175.         window := control^^.contrlOwner;
  176.         doc := TTEDocument(gApplication.DocList.FindDoc(window));
  177.         hTE := doc.GetTEHandle;
  178.         CASE part OF
  179.             inUpButton, inDownButton:        { a few pixels }
  180.                 amount := kButtonScroll;
  181.             inPageUp, inPageDown:            { a page width }
  182.                 WITH hTE^^.viewRect DO
  183.                     amount := (right - left);
  184.         END;
  185.         IF ((part = inDownButton) OR (part = inPageDown)) THEN
  186.             amount := -amount;        { reverse direction }
  187.         CommonAction(control, amount);
  188.         IF (amount <> 0) THEN
  189.             TEScroll(amount, 0, hTE);
  190.     END;
  191. END; { HActionProc }
  192.  
  193. { Gets called from our assembly language routine, AsmClickLoop, which in turn }
  194. { is called by the TEClick toolbox routine. Saves the window's clip region, }
  195. { sets it to the portRect, adjusts the scrollbar values to match the TE scroll }
  196. { amount, then restores the clip region. }
  197.  
  198. {$S Main}
  199. PROCEDURE PascalClickLoop;
  200. VAR
  201.     region: RgnHandle;
  202.     wind: WindowPtr;
  203.     doc: TTEDocument;
  204. BEGIN
  205.     wind := FrontWindow;
  206.     doc := TTEDocument(gApplication.DocList.FindDoc(wind));
  207.     region := NewRgn;
  208.     GetClip(region);                { save the old clip }
  209.     ClipRect(wind^.portRect);        { set the new clip }
  210.     doc.AdjustScrollValues(FALSE);    { pass false for canRedraw }
  211.     SetClip(region);                { restore the old clip }
  212.     DisposeRgn(region);
  213. END; { PascalClickLoop }
  214.  
  215. { Gets called from our assembly language routine, AsmClickLoop, which is in }
  216. { turn called by the TEClick toolbox routine. It returns the address of the }
  217. { default clickLoop routine that was put into the TERec by TEAutoView to }
  218. { AsmClickLoop so that it can call it. }
  219.  
  220. {$S Main}
  221. FUNCTION GetOldClickLoop:TEClickLoopUPP;
  222. VAR
  223.     doc: TTEDocument;
  224. BEGIN
  225.     doc := TTEDocument(gApplication.DocList.FindDoc(FrontWindow));
  226.     IF (doc = NIL) THEN
  227.         GetOldClickLoop := nil
  228.     ELSE
  229.         GetOldClickLoop := doc.GetClickLoop;
  230. END; { GetOldClickLoop }
  231.  
  232.  
  233. (********************************************************************************************)
  234. (*        T T E D o c u m e n t                                                                *)
  235. (********************************************************************************************)
  236.  
  237. {$S Initialize}
  238. {-----------------------------------+
  239. |    ITEDocument                        |
  240. +-----------------------------------}
  241. PROCEDURE TTEDocument.ITEDocument(resID:integer);
  242. VAR
  243.     good:Boolean;
  244.     destRect, viewRect: Rect;
  245. BEGIN
  246.  
  247.     IDocument(resID);
  248.  
  249.     SetPort(fDocWindow);
  250.     GetTERect(viewRect);
  251.     destRect := viewRect;
  252.     destRect.right := destRect.left + kMaxDocWidth;
  253.     fDocTE := TENew(destRect, viewRect);
  254.  
  255.     good := (fDocTE <> NIL);        { if TENew succeeded, we have a good document. }
  256.     IF good THEN BEGIN                { good document? — get scrollbars  }
  257.         AdjustViewRect;
  258.         TEAutoView(TRUE, fDocTE);
  259.         fDocClick := fDocTE^^.clickLoop;
  260.         fDocTE^^.clickLoop := @AsmClickLoop;
  261.         fDocVScroll := GetNewControl(rVScroll, fDocWindow);
  262.         good := (fDocVScroll <> NIL);
  263.     END;
  264.     IF good THEN BEGIN
  265.         fDocHScroll := GetNewControl(rHScroll, fDocWindow);
  266.         good := (fDocHScroll <> NIL);
  267.     END;
  268.  
  269.     IF good THEN BEGIN                { good? — adjust & draw the controls, draw the window }
  270.         AdjustScrollValues(FALSE);
  271.         ShowWindow(fDocWindow);
  272.     END ELSE BEGIN                    { tell user we failed }
  273.         AlertUser(kTEDocErrStrings,eNoWindow);
  274.     END;
  275. END;
  276.  
  277. {$S Main}
  278. {-----------------------------------+
  279. |    Free                            |
  280. +-----------------------------------}
  281. PROCEDURE TTEDocument.Free; OVERRIDE;
  282. BEGIN
  283.     HideWindow(fDocWindow);
  284.     IF fDocTE <> NIL THEN
  285.       TEDispose(fDocTE);            { dispose the TEHandle if we got far enough to make one  }
  286.     IF fDocVScroll <> NIL THEN
  287.       DisposeControl(fDocVScroll);
  288.     IF fDocHScroll <> NIL THEN
  289.       DisposeControl(fDocHScroll);
  290.     INHERITED Free;
  291. END;
  292.  
  293. {$S Main}
  294. {-----------------------------------+
  295. |    DoZoom                            |
  296. +-----------------------------------}
  297. PROCEDURE TTEDocument.DoZoom(partCode:integer); OVERRIDE;
  298. BEGIN
  299.     EraseRect(fDocWindow^.portRect);
  300.     ZoomWindow(fDocWindow, partCode, (fDocWindow = FrontWindow));
  301.     ResizeWindow;    {after this, only thing valid is scrollbars.}
  302. END;
  303.  
  304. {$S Main}
  305. {-----------------------------------+
  306. |    DoGrow                            |
  307. +-----------------------------------}
  308.  
  309. {Called when a mouseDown occurs in the grow box of an active window. In
  310.  order to eliminate any 'flicker', we want to invalidate only what is
  311.  necessary. Since ResizeWindow invalidates the whole portRect, we save
  312.  the old TE viewRect, intersect it with the new TE viewRect, and
  313.  remove the result from the update region. However, we must make sure
  314.  that any old update region that might have been around gets put back.}
  315.  
  316. PROCEDURE TTEDocument.DoGrow(theEvent:EventRecord); OVERRIDE;
  317. VAR
  318.     growResult: longint;
  319.     tempRect:    Rect;
  320.     tempRgn:    RgnHandle;
  321.     
  322.     PROCEDURE GetLocalUpdateRgn(aRgn:RgnHandle);
  323.     BEGIN
  324.         CopyRgn(WindowPeek(fDocWindow)^.updateRgn, aRgn);    {save old update region}
  325.         WITH fDocWindow^.portBits.bounds DO
  326.             OffsetRgn(aRgn, left, top);                        {convert to local coords}
  327.     END;
  328.  
  329. BEGIN
  330.     tempRect := qd.screenBits.bounds;
  331.     tempRect.left := kMinDocDim;
  332.     tempRect.top := kMinDocDim;
  333.     growResult := GrowWindow(fDocWindow, theEvent.where, tempRect);
  334.     { see if it really changed size  }
  335.     IF growResult <> 0 THEN BEGIN
  336.         tempRect := fDocTE^^.viewRect;
  337.         tempRgn := NewRgn;
  338.         GetLocalUpdateRgn(tempRgn);
  339.         SizeWindow(fDocWindow, LoWord(growResult), HiWord(growResult), TRUE);
  340.         ResizeWindow;    {after this, only thing valid is scrollbars.}
  341.         IF SectRect(tempRect, fDocTE^^.viewRect, tempRect) THEN;
  342.         ValidRect(tempRect);
  343.         InvalRgn(tempRgn);
  344.         DisposeRgn(tempRgn);
  345.     END;
  346. END;
  347.  
  348. {$S Main}
  349. {-----------------------------------+
  350. |    DoContent                        |
  351. +-----------------------------------}
  352. PROCEDURE TTEDocument.DoContent(theEvent:EventRecord); OVERRIDE;
  353. VAR
  354.     mouse:        Point;
  355.     control:    ControlHandle;
  356.     part,value:    integer;
  357.     shiftDown:    Boolean;
  358.     upp:        ControlActionUPP;
  359.  
  360. BEGIN
  361.     SetPort(fDocWindow);
  362.     mouse := theEvent.where;            { get the click position  }
  363.     GlobalToLocal(mouse);
  364.     part := FindControl(mouse, fDocWindow, control);
  365.     CASE part OF
  366.           0: BEGIN                        { not in a control }
  367.             { see if we need to extend the selection  }
  368.             shiftDown := BAnd(theEvent.modifiers, shiftKey) <> 0;    { extend if Shift is down  }
  369.             TEClick(mouse, shiftDown, fDocTE);
  370.         END;
  371.         inThumb: BEGIN
  372.             value := GetControlValue(control);
  373.             part := TrackControl(control, mouse, NIL);
  374.             IF part <> 0 THEN BEGIN
  375.                 value := value - GetControlValue(control);
  376.                 { value now has CHANGE in value; if value changed, scroll  }
  377.                 IF value <> 0 THEN BEGIN
  378.                     IF control = fDocVScroll THEN BEGIN
  379.                         TEScroll(0, value * fDocTE^^.lineHeight, fDocTE);
  380.                     END ELSE BEGIN
  381.                         TEScroll(value, 0, fDocTE);
  382.                     END;
  383.                 END;
  384.             END;
  385.         END;
  386.         OTHERWISE BEGIN                        { they clicked in an arrow, so track & scroll  }
  387.             IF control = fDocVScroll THEN
  388.                 upp := NewControlActionProc(@VActionProc)
  389.              ELSE
  390.                 upp := NewControlActionProc(@HActionProc);
  391.             value := TrackControl(control, mouse, upp);
  392.             DisposeRoutineDescriptor(upp);
  393.         END;
  394.     END;
  395. END;
  396.  
  397. {$S Main}
  398. {-----------------------------------+
  399. |    DoKeyDown                        |
  400. +-----------------------------------}
  401. PROCEDURE TTEDocument.DoKeyDown(theEvent:EventRecord); OVERRIDE;
  402. VAR
  403.     key: char;
  404. BEGIN
  405.     IF BAnd(theEvent.modifiers, cmdKey) <> 1 THEN BEGIN    { don't process command characters }
  406.         key := char(BAnd(theEvent.message, charCodeMask));
  407.         { we have a char. for our window; see if we are still below TextEdit’s }
  408.         { limit for the number of characters }
  409.         WITH fdocTE^^ DO BEGIN
  410.             IF ((key = kDelChar) OR ((teLength - selEnd - selStart) + 1 < kMaxTELength)) THEN BEGIN
  411.                 TEKey(key, fDocTE);
  412.                 AdjustScrollbars(FALSE);
  413.                 AdjustTE;
  414.             END ELSE BEGIN
  415.                 AlertUser(kTEDocErrStrings,eExceedChar);
  416.             END;
  417.         END;
  418.     END;
  419. END;
  420.  
  421. {$S Main}
  422. {-----------------------------------+
  423. |    DoActivate                        |
  424. +-----------------------------------}
  425. PROCEDURE TTEDocument.DoActivate(becomingActive:Boolean); OVERRIDE;
  426. VAR
  427.     tempRgn:    RgnHandle;
  428.     clipRgn:    RgnHandle;
  429.     growRect:    Rect;
  430. BEGIN
  431.     IF becomingActive THEN BEGIN
  432.  
  433.         { since we don’t want TEActivate to draw a selection in an area where }
  434.         { we’re going to erase and redraw, we’ll clip out the update region }
  435.         { before calling it. }
  436.         tempRgn := NewRgn;
  437.         clipRgn := NewRgn;
  438.         { save old update region }
  439.         CopyRgn(WindowPeek(fDocWindow)^.updateRgn, tempRgn);
  440.         { put it in local coords }
  441.         WITH fDocWindow^.portBits.bounds DO
  442.             OffsetRgn(tempRgn, left, top);
  443.         GetClip(clipRgn);
  444.         { subtract updateRgn from clipRgn }
  445.         DiffRgn(clipRgn, tempRgn, tempRgn);
  446.         { make it the new clipRgn }
  447.         SetClip(tempRgn);
  448.         TEActivate(fDocTE);
  449.         { restore the full-blown clipRgn }
  450.         SetClip(clipRgn);
  451.         { get rid of temp regions }
  452.         DisposeRgn(tempRgn);
  453.         DisposeRgn(clipRgn);
  454.  
  455.         { the controls must be redrawn on activation: }
  456.         fDocVScroll^^.contrlVis := kControlVisible;
  457.         fDocHScroll^^.contrlVis := kControlVisible;
  458.         InvalRect(fDocVScroll^^.contrlRect);
  459.         InvalRect(fDocHScroll^^.contrlRect);
  460.         { the growbox needs to be redrawn on activation: }
  461.         growRect := fDocWindow^.portRect;
  462.         { adjust for the scrollbars }
  463.         WITH growRect DO BEGIN
  464.             top := bottom - kScrollbarAdjust;
  465.             left := right - kScrollbarAdjust;
  466.         END;
  467.         InvalRect(growRect);
  468.     END ELSE BEGIN                { becoming Inactive }
  469.         TEDeactivate(fDocTE);
  470.         { the controls must be hidden on deactivation: }
  471.         HideControl(fDocVScroll);
  472.         HideControl(fDocHScroll);
  473.         { we draw grow icon immediately, since we deactivate controls }
  474.         { immediately, and the update delay looks funny }
  475.         DrawGrowIcon(fDocWindow);
  476.     END;
  477. END;
  478.  
  479. {$S Main}
  480. {-----------------------------------+
  481. |    DoIdle                            |
  482. +-----------------------------------}
  483. PROCEDURE TTEDocument.DoIdle; OVERRIDE;
  484. BEGIN
  485.     TEIdle(fDocTE);
  486. END;
  487.  
  488. {$S Main}
  489. {-----------------------------------+
  490. |    DoUpdate                        |
  491. +-----------------------------------}
  492. PROCEDURE TTEDocument.DoUpdate; OVERRIDE;
  493. BEGIN
  494.     BeginUpdate(fDocWindow);                    { this sets up the visRgn }
  495.     IF NOT EmptyRgn(fDocWindow^.visRgn) THEN    { draw if updating needs to be done }
  496.         DrawWindow;
  497.     EndUpdate(fDocWindow);
  498. END;
  499.  
  500. {$S Main}
  501. {-----------------------------------+
  502. |    DoCut                            |
  503. +-----------------------------------}
  504. PROCEDURE TTEDocument.DoCut; OVERRIDE;
  505. VAR
  506.     total, contig:    longint;
  507. BEGIN
  508.     IF (ZeroScrap = noErr) THEN BEGIN
  509.         PurgeSpace(total, contig);
  510.         IF (fDocTE^^.selEnd - fDocTE^^.selStart + kTESlop > contig) THEN BEGIN
  511.             AlertUser(kTEDocErrStrings,eNoSpaceCut);
  512.         END ELSE BEGIN
  513.             TECut(fDocTE);
  514.             IF (TEToScrap <> noErr) THEN BEGIN
  515.                 AlertUser(kTEDocErrStrings,eNoCut);
  516.                 IF Boolean(ZeroScrap) THEN;
  517.             END;
  518.         END;
  519.     END;
  520.     AdjustScrollbars(FALSE);
  521.     AdjustTE;
  522. END;
  523.  
  524. {$S Main}
  525. {-----------------------------------+
  526. |    DoCopy                            |
  527. +-----------------------------------}
  528. PROCEDURE TTEDocument.DoCopy; OVERRIDE;
  529. BEGIN
  530.     IF (ZeroScrap = noErr) THEN BEGIN
  531.         TECopy(fDocTE);                { after copying, export the TE scrap }
  532.         IF (TEToScrap <> noErr) THEN BEGIN
  533.             AlertUser(kTEDocErrStrings,eNoCopy);
  534.             IF Boolean(ZeroScrap) THEN;
  535.         END;
  536.     END;
  537.     AdjustScrollbars(FALSE);
  538.     AdjustTE;
  539. END;
  540.  
  541. {$S Main}
  542. {-----------------------------------+
  543. |    DoPaste                            |
  544. +-----------------------------------}
  545. PROCEDURE TTEDocument.DoPaste; OVERRIDE;
  546. VAR
  547.     aHandle:    Handle;
  548.     oldsize,
  549.     newSize:    longint;
  550.     saveErr:    OSErr;
  551. BEGIN
  552.     IF (TEFromScrap = noErr) THEN BEGIN
  553.         WITH fDocTE^^ DO BEGIN
  554.             IF (TEGetScrapLength + (teLength - (selEnd - selStart)) > kMaxTELength) THEN BEGIN
  555.                 AlertUser(kTEDocErrStrings,eExceedPaste);
  556.             END ELSE BEGIN
  557.                 aHandle := Handle(TEGetText(fDocTE));
  558.                 oldSize := GetHandleSize(aHandle);
  559.                 newSize := oldSize + TEGetScrapLength + kTESlop;
  560.                 SetHandleSize(aHandle, newSize);
  561.                 saveErr := MemError;
  562.                 SetHandleSize(aHandle, oldSize);
  563.                 IF (saveErr <> noErr) THEN BEGIN
  564.                     AlertUser(kTEDocErrStrings,eNoSpacePaste);
  565.                 END ELSE BEGIN
  566.                     TEPaste(fDocTE);
  567.                 END;
  568.             END;
  569.         END;
  570.     END ELSE BEGIN
  571.         AlertUser(kTEDocErrStrings,eNoPaste);
  572.     END;
  573.     AdjustScrollbars(FALSE);
  574.     AdjustTE;
  575. END;
  576.  
  577. {$S Main}
  578. {-----------------------------------+
  579. |    DoClear                            |
  580. +-----------------------------------}
  581. PROCEDURE TTEDocument.DoClear; OVERRIDE;
  582. BEGIN
  583.     TEDelete(fDocTE);
  584.     AdjustScrollbars(FALSE);
  585.     AdjustTE;
  586. END;
  587.  
  588. {$S Main}
  589. {-----------------------------------+
  590. |    HaveSelection                    |
  591. +-----------------------------------}
  592. FUNCTION TTEDocument.HaveSelection:Boolean; OVERRIDE;
  593. BEGIN
  594.     IF (fDocTE^^.selStart < fDocTE^^.selEnd) THEN
  595.         HaveSelection := TRUE
  596.     ELSE
  597.         HaveSelection := FALSE;
  598. END;
  599.  
  600. {$S Main}
  601. {-----------------------------------+
  602. |    CalcIdle                        |
  603. +-----------------------------------}
  604. FUNCTION TTEDocument.CalcIdle:Longint; OVERRIDE;
  605. BEGIN
  606.     IF NOT(HaveSelection) THEN BEGIN
  607.         CalcIdle := GetCaretTime;
  608.     END ELSE BEGIN
  609.         CalcIdle := $7FFFFFFF;
  610.     END;
  611. END;
  612.  
  613. {$S Main}
  614. {-----------------------------------+
  615. |    AdjustScrollValues                |
  616. +-----------------------------------}
  617. { Simply call the common adjust routine for the vertical and horizontal scrollbars. }
  618. PROCEDURE TTEDocument.AdjustScrollValues(mustRedraw:Boolean);
  619. BEGIN
  620.     AdjustHV(true, mustRedraw);
  621.     AdjustHV(false, mustRedraw);
  622. END;
  623.  
  624. {$S Main}
  625. {-----------------------------------+
  626. |    GetClickLoop                        |
  627. +-----------------------------------}
  628. FUNCTION TTEDocument.GetClickLoop:TEClickLoopUPP;
  629. BEGIN
  630.     GetClickLoop := fDocClick;
  631. END;
  632.  
  633. {$S Main}
  634. {-----------------------------------+
  635. |    GetTEHandle                        |
  636. +-----------------------------------}
  637. FUNCTION TTEDocument.GetTEHandle:TEHandle;
  638. BEGIN
  639.     GetTEHandle := fDocTE;
  640. END;
  641.  
  642. {$S Main}
  643. {-----------------------------------+
  644. |    GetVisTERgn                        |
  645. +-----------------------------------}
  646. PROCEDURE TTEDocument.GetVisTERgn(rgn:RgnHandle);
  647. VAR
  648.     teRect:    Rect;
  649. BEGIN
  650.     teRect := fDocTE^^.viewRect;        { get a local copy of viewRect }
  651.     SetPort(fDocWindow);            { make sure we have right port }
  652.     LocalToGlobal(teRect.topLeft);
  653.     LocalToGlobal(teRect.botRight);
  654.     RectRgn(rgn, teRect);
  655.     { we temporarily change the port’s origin to “globalfy” the visRgn }
  656.     WITH fDocWindow^.portbits.bounds DO
  657.         SetOrigin(-left,-top);
  658.     SectRgn(rgn, fDocWindow^.visRgn, rgn);
  659.     SetOrigin(0, 0);
  660. END;
  661.  
  662. {$S Main}
  663. {-----------------------------------+
  664. |    GetTERect                        |
  665. +-----------------------------------}
  666. { Return a rectangle that is inset from the portRect by the size of }
  667. { the scrollbars and a little extra margin. }
  668. PROCEDURE TTEDocument.GetTERect(VAR teRect:Rect);
  669. BEGIN
  670.     teRect := fDocWindow^.portRect;
  671.     InsetRect(teRect, kTextMargin, kTextMargin);        { adjust for margin  }
  672.     WITH teRect DO BEGIN
  673.         bottom := bottom - kScrollbarAdjust;            { and for the scrollbars }
  674.         right := right - kScrollbarAdjust;
  675.     END;
  676. END;
  677.  
  678. {$S Main}
  679. {-----------------------------------+
  680. |    AdjustTE                        |
  681. +-----------------------------------}
  682. { Scroll the TERec around to match up to the potentially updated scrollbar }
  683. { values. This is really useful when the window has been resized such that the }
  684. { scrollbars became inactive but the TERec was already scrolled. }
  685. PROCEDURE TTEDocument.AdjustTE;
  686. BEGIN
  687.     WITH fDocTE^^ DO BEGIN
  688.         TEScroll((viewRect.left - destRect.left) - GetControlValue(fDocHScroll),
  689.                  (viewRect.top - destRect.top) - (GetControlValue(fDocVScroll) * lineHeight),
  690.                  fDocTE);
  691.     END;
  692. END;
  693.  
  694. {$S Main}
  695. {-----------------------------------+
  696. |    DrawWindow                        |
  697. +-----------------------------------}
  698. PROCEDURE TTEDocument.DrawWindow;
  699. BEGIN
  700.     SetPort(fDocWindow);
  701.     EraseRect(fDocWindow^.portRect);    { As per TextEdit chapter of Inside Macintosh }
  702.     DrawControls(fDocWindow);            { This ordering makes for a better appearance }
  703.     DrawGrowIcon(fDocWindow);
  704.     TEUpdate(fDocWindow^.portRect, fDocTE);
  705. END;
  706.  
  707. {$S Main}
  708. {-----------------------------------+
  709. |    AdjustViewRect                    |
  710. +-----------------------------------}
  711. { Update the TERec's view rect so that it is the greatest multiple of }
  712. { the lineHeight that still fits in the old viewRect. }
  713. PROCEDURE TTEDocument.AdjustViewRect;
  714. BEGIN
  715.     WITH fDocTE^^,fDocTE^^.viewRect DO BEGIN
  716.         bottom := (((bottom - top) DIV lineHeight) * lineHeight) + top;
  717.     END;
  718. END;
  719.  
  720. {$S Main}
  721. {-----------------------------------+
  722. |    ResizeWindow                    |
  723. +-----------------------------------}
  724.  
  725. {Called when the window has been resized to fix up the controls and content.
  726.  This routine moves and resizes the scrollbars, adjusts their values, scrolls
  727.  the TERecord in case we grew the window out from under it, invalidates the
  728.  entire port, and then validates the areas under the scrollbars.}
  729.  
  730. PROCEDURE TTEDocument.ResizeWindow;
  731. BEGIN
  732.     AdjustScrollbars(TRUE);                { adjust, redraw anyway  }
  733.     AdjustTE;
  734.     InvalRect(fDocWindow^.portRect);    { invalidate the whole content  }
  735.     { the scrollbars were taken care of by AdjustScrollbars, so validate ’em  }
  736.     ValidRect(fDocVScroll^^.contrlRect);
  737.     ValidRect(fDocHScroll^^.contrlRect);
  738. END;
  739.  
  740. {$S Main}
  741. {-----------------------------------+
  742. |    AdjustHV                        |
  743. +-----------------------------------}
  744. { Calculate the new control maximum value and current value, whether it is the horizontal or }
  745. { vertical scrollbar. The vertical max is calculated by comparing the number of lines to the }
  746. { vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document }
  747. { width to the width of the viewRect. The current values are set by comparing the offset between }
  748. { the view and destination rects. If necessary, redraw the control by calling ShowControl. }
  749. PROCEDURE TTEDocument.AdjustHV(isVert, mustRedraw:Boolean);
  750. VAR
  751.     value, lines, max:    integer;
  752.     oldValue, oldMax:    integer;
  753.     hTE:                TEHandle;
  754.     control:            ControlHandle;
  755. BEGIN
  756.     IF isVert THEN
  757.         control := fDocVScroll
  758.     ELSE
  759.         control := fDocHScroll;
  760.     oldValue := GetControlValue(control);
  761.     oldMax := GetControlMaximum(control);
  762.     hTE := fDocTE;
  763.     WITH hTE^^ DO BEGIN
  764.         IF isVert THEN BEGIN
  765.             lines := nLines;
  766.             { since nLines isn’t right if the last character is a return, check for that case }
  767.             { MUST perform a short circuit check here, or we get a check error. }
  768.             IF ((teLength > 0) & (CharsHandle(hText)^^[teLength-1] = kCrChar)) THEN
  769.                 lines := lines + 1;
  770.             WITH viewRect DO max := lines - ((bottom - top) DIV lineHeight);
  771.         END    ELSE BEGIN
  772.             WITH viewRect DO max := kMaxDocWidth - (right - left);
  773.         END;
  774.     END;
  775.     IF max < 0 THEN max := 0;
  776.     SetControlMaximum(control, max);
  777.     
  778.     WITH hTE^^ DO BEGIN
  779.         IF isVert THEN BEGIN
  780.             value := (viewRect.top - destRect.top) DIV lineHeight
  781.         END ELSE BEGIN
  782.             value := viewRect.left - destRect.left;
  783.         END;
  784.     END;
  785.     
  786.     {Pin the value to within range}
  787.     IF value < 0 THEN value := 0;
  788.     IF value > max THEN value := max;
  789.     
  790.     SetControlValue(control, value);
  791.     { now redraw the control if asked to or if a setting changed  }
  792.     IF ((mustRedraw) OR ((max <> oldMax) OR (value <> oldValue))) THEN
  793.         ShowControl(control);
  794. END;
  795.  
  796. {$S Main}
  797. {-----------------------------------+
  798. |    AdjustScrollSizes                |
  799. +-----------------------------------}
  800. { Re-calculate the position and size of the viewRect and the scrollbars. }
  801. { kScrollTweek compensates for off-by-one requirements of the scrollbars }
  802. { to have borders coincide with the growbox. }
  803. PROCEDURE TTEDocument.AdjustScrollSizes;
  804. VAR
  805.     teRect:    Rect;
  806. BEGIN    
  807.     GetTERect(teRect);
  808.     fDocTE^^.viewRect := teRect;
  809.     AdjustViewRect;
  810.     WITH fDocWindow^.portRect DO BEGIN
  811.         MoveControl(fDocVScroll, right - kScrollbarAdjust, -1);
  812.         SizeControl(fDocVScroll, kScrollbarWidth, bottom - top - kGrowboxAdjust + kScrollTweek);
  813.         MoveControl(fDocHScroll, -1, bottom - kScrollbarAdjust);
  814.         SizeControl(fDocHScroll, right - left - kGrowboxAdjust + kScrollTweek, kScrollbarWidth);
  815.     END;
  816. END;
  817.  
  818. {$S Main}
  819. {-----------------------------------+
  820. |    AdjustScrollbars                |
  821. +-----------------------------------}
  822. { Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them }
  823. { and we don't want that). If the controls are to be resized as well, call the procedure to do that, }
  824. { then call the procedure to adjust the maximum and current values. Finally re-enable the controls }
  825. { by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be }
  826. { necessary). }
  827. PROCEDURE TTEDocument.AdjustScrollbars(needsResize:Boolean);
  828. BEGIN
  829.     { First, turn visibility of scrollbars off so we won’t get unwanted redrawing  }
  830.     fDocVScroll^^.contrlVis := kControlInvisible;
  831.     fDocHScroll^^.contrlVis := kControlInvisible;
  832.     IF needsResize THEN BEGIN
  833.         AdjustScrollSizes;
  834.     END;
  835.     AdjustScrollValues(needsResize);
  836.     { Now, restore visibility in case we never had to draw during adjustment  }
  837.     fDocVScroll^^.contrlVis := kControlVisible;
  838.     fDocHScroll^^.contrlVis := kControlVisible;
  839. END;
  840.